package com.zhaofujun.filequeue;


import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;

public class StatusFileMapper extends BaseFileMapper {

    private final WriteNumRef writeNumRef;
    private final ReadNums readNums;


    private final MappedByteBuffer statusMappedByteBuffer;


    public int SIZE = Integer.BYTES + Long.BYTES + Long.BYTES + Long.BYTES * 16;

    private MappedByteBuffer getStatusMappedByteBuffer(String baseDir) throws IOException {
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(getStatusMappedFile(baseDir), "rw");
             FileChannel channel = randomAccessFile.getChannel()) {
            return channel.map(FileChannel.MapMode.READ_WRITE, 0, SIZE);
        }
    }

    private File getStatusMappedFile(String baseDir) {
        return Paths.get(baseDir, "status.rw").toFile();
    }

    public StatusFileMapper(String baseDir, int segmentSize) throws IOException {
        super(baseDir, segmentSize);

        //系统预置16个readPosition
        //位置信息存放的内容：[4:segmentSize][8:writePosition][8:writeOffset][8:readPosition1][8:readPosition2]//如果有多个readPosition，依次类推


        if (getStatusMappedFile(baseDir).exists()) {
            //文件存在，则读取文件中的WritePosition信息
            this.statusMappedByteBuffer = getStatusMappedByteBuffer(baseDir);

            //读取文件中的segmentSize信息，忽略传入的segmentSize
            this.segmentSize = statusMappedByteBuffer.getInt(0);

        } else {
            //文件不存在
            this.statusMappedByteBuffer = getStatusMappedByteBuffer(baseDir);

            //写入segmentSize信息
            statusMappedByteBuffer.putInt(0, segmentSize);
            statusMappedByteBuffer.putLong(12, calculateFirstOffset());

        }
        //初始化写指针的信息
        //从4开始读，读出两个Long，分别是segmentSize和writePosition
        long writePosition = statusMappedByteBuffer.getLong(4);
        long writeOffset = statusMappedByteBuffer.getLong(12);

        //读取文件中的writePosition信息
        this.writeNumRef = new WriteNumRef(writePosition, writeOffset);


        //初始化readNums
        //从20开始读，读16个readPosition
        long[] readPositions = new long[16];
        for (int i = 0; i < 16; i++) {
            readPositions[i] = this.statusMappedByteBuffer.getLong(20 + i * Long.BYTES);
        }
        this.readNums = new ReadNums(readPositions);


    }


    public DataInfo preallocate(byte[] bytes, SegmentFileMapper segmentContext) {
        DataInfo dataInfo = new DataInfo();

        //包装数据
        dataInfo.setData(bytes);


        //包装数据信息的头信息
        WriteInfo writeInfo = writeNumRef.getAndSet(bytes.length, segmentContext);
        dataInfo.setHeadInfo(new HeadInfo(writeInfo.getNum(), writeInfo.getOffset(), dataInfo.getData().length));
        //包装数据信息的片段信息
        SegmentInfo segmentInfo = segmentContext.calculateSegmentInfo(writeInfo.getNum());
        dataInfo.setSegmentInfo(segmentInfo);


        return dataInfo;
    }

    public void release() {
        release(statusMappedByteBuffer);
    }

    public void updateWritePosition() {
        WriteInfo writeInfo = writeNumRef.getNumRef();
        statusMappedByteBuffer.putLong(4, writeInfo.getNum());
        statusMappedByteBuffer.putLong(12, writeInfo.getOffset());
    }

    public void updateReadPosition(int no) {
        long readNum = readNums.get(no);
        statusMappedByteBuffer.putLong(20 + no * Long.BYTES, readNum);
    }


    public WriteNumRef getWriteNumRef() {
        return writeNumRef;
    }

    public ReadNums getReadNums() {
        return readNums;
    }
}

